home *** CD-ROM | disk | FTP | other *** search
/ AGA Toolkit '97 / The AGA Toolkit '97.iso / miscellaneous / science / maths / calc / source / token.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-09-07  |  12.2 KB  |  605 lines

  1. /*
  2.  * Copyright (c) 1994 David I. Bell
  3.  * Permission is granted to use, distribute, or modify this source,
  4.  * provided that this copyright notice remains intact.
  5.  *
  6.  * Read input file characters into tokens
  7.  */
  8.  
  9. #include "stdarg.h"
  10. #include "calc.h"
  11. #include "token.h"
  12. #include "string.h"
  13.  
  14.  
  15. #define isletter(ch)    ((((ch) >= 'a') && ((ch) <= 'z')) || \
  16.                 (((ch) >= 'A') && ((ch) <= 'Z')))
  17. #define isdigit(ch)    (((ch) >= '0') && ((ch) <= '9'))
  18. #define issymbol(ch)    (isletter(ch) || isdigit(ch) || ((ch) == '_'))
  19.  
  20.  
  21. /*
  22.  * Current token.
  23.  */
  24. static struct {
  25.     short t_type;        /* type of token */
  26.     char *t_str;        /* string value or symbol name */
  27.     long t_numindex;    /* index of numeric value */
  28. } curtoken;
  29.  
  30.  
  31. static BOOL rescan;        /* TRUE to reread current token */
  32. static BOOL newlines;        /* TRUE to return newlines as tokens */
  33. static BOOL allsyms;        /* TRUE if always want a symbol token */
  34. static STRINGHEAD strings;    /* list of constant strings */
  35. static char *numbuf;        /* buffer for numeric tokens */
  36. static long numbufsize;        /* current size of numeric buffer */
  37.  
  38. long errorcount;        /* number of compilation errors */
  39.  
  40.  
  41. /*
  42.  * Table of keywords
  43.  */
  44. struct keyword {
  45.     char *k_name;    /* keyword name */
  46.     int k_token;    /* token number */
  47. };
  48.  
  49. static struct keyword keywords[] = {
  50.     "if",        T_IF,
  51.     "else",        T_ELSE,
  52.     "for",        T_FOR,
  53.     "while",    T_WHILE,
  54.     "do",        T_DO,
  55.     "continue",    T_CONTINUE,
  56.     "break",    T_BREAK,
  57.     "goto",        T_GOTO,
  58.     "return",    T_RETURN,
  59.     "local",    T_LOCAL,
  60.     "global",    T_GLOBAL,
  61.     "static",    T_STATIC,
  62.     "switch",    T_SWITCH,
  63.     "case",        T_CASE,
  64.     "default",    T_DEFAULT,
  65.     "quit",        T_QUIT,
  66.     "exit",        T_QUIT,
  67.     "define",    T_DEFINE,
  68.     "read",        T_READ,
  69.     "show",        T_SHOW,
  70.     "help",        T_HELP,
  71.     "write",    T_WRITE,
  72.     "mat",        T_MAT,
  73.     "obj",        T_OBJ,
  74.     "print",    T_PRINT,
  75.     "cd",        T_CD,
  76.     NULL,        0
  77. };
  78.  
  79.  
  80. static void eatcomment MATH_PROTO((void));
  81. static void eatstring MATH_PROTO((int quotechar));
  82. static int eatsymbol MATH_PROTO((void));
  83. static int eatnumber MATH_PROTO((void));
  84.  
  85.  
  86. /*
  87.  * Initialize all token information.
  88.  */
  89. void
  90. inittokens()
  91. {
  92.     initstr(&strings);
  93.     newlines = FALSE;
  94.     allsyms = FALSE;
  95.     rescan = FALSE;
  96.     setprompt(PROMPT1);
  97. }
  98.  
  99.  
  100. /*
  101.  * Set the new token mode according to the specified flag, and return the
  102.  * previous value of the flag.
  103.  */
  104. int
  105. tokenmode(flag)
  106.     int flag;
  107. {
  108.     int    oldflag;
  109.  
  110.     oldflag = TM_DEFAULT;
  111.     if (newlines)
  112.         oldflag |= TM_NEWLINES;
  113.     if (allsyms)
  114.         oldflag |= TM_ALLSYMS;
  115.     newlines = FALSE;
  116.     allsyms = FALSE;
  117.     if (flag & TM_NEWLINES)
  118.         newlines = TRUE;
  119.     if (flag & TM_ALLSYMS)
  120.         allsyms = TRUE;
  121.     setprompt(newlines ? PROMPT1 : PROMPT2);
  122.     return oldflag;
  123. }
  124.  
  125.  
  126. /*
  127.  * Routine to read in the next token from the input stream.
  128.  * The type of token is returned as a value.  If the token is a string or
  129.  * symbol name, information is saved so that the value can be retrieved.
  130.  */
  131. int
  132. gettoken()
  133. {
  134.     int ch;            /* current input character */
  135.     int type;        /* token type */
  136.  
  137.     if (rescan) {        /* rescanning */
  138.         rescan = FALSE;
  139.         return curtoken.t_type;
  140.     }
  141.     curtoken.t_str = NULL;
  142.     curtoken.t_numindex = 0;
  143.     type = T_NULL;
  144.     while (type == T_NULL) {
  145.         ch = nextchar();
  146.         if (allsyms && ((ch!=' ') && (ch!=';') && (ch!='"') && (ch!='\n'))) {
  147.             reread();
  148.             type = eatsymbol();
  149.             break;
  150.         }
  151.         switch (ch) {
  152.         case ' ':
  153.         case '\t':
  154.         case '\0':
  155.             break;
  156.         case '\n':
  157.             if (newlines)
  158.                 type = T_NEWLINE;
  159.             break;
  160.         case EOF: type = T_EOF; break;
  161.         case '{': type = T_LEFTBRACE; break;
  162.         case '}': type = T_RIGHTBRACE; break;
  163.         case '(': type = T_LEFTPAREN; break;
  164.         case ')': type = T_RIGHTPAREN; break;
  165.         case '[': type = T_LEFTBRACKET; break;
  166.         case ']': type = T_RIGHTBRACKET; break;
  167.         case ';': type = T_SEMICOLON; break;
  168.         case ':': type = T_COLON; break;
  169.         case ',': type = T_COMMA; break;
  170.         case '?': type = T_QUESTIONMARK; break;
  171.         case '"':
  172.         case '\'':
  173.             type = T_STRING;
  174.             eatstring(ch);
  175.             break;
  176.         case '^':
  177.             switch (nextchar()) {
  178.                 case '=': type = T_POWEREQUALS; break;
  179.                 default: type = T_POWER; reread();
  180.             }
  181.             break;
  182.         case '=':
  183.             switch (nextchar()) {
  184.                 case '=': type = T_EQ; break;
  185.                 default: type = T_ASSIGN; reread();
  186.             }
  187.             break;
  188.         case '+':
  189.             switch (nextchar()) {
  190.                 case '+': type = T_PLUSPLUS; break;
  191.                 case '=': type = T_PLUSEQUALS; break;
  192.                 default: type = T_PLUS; reread();
  193.             }
  194.             break;
  195.         case '-':
  196.             switch (nextchar()) {
  197.                 case '-': type = T_MINUSMINUS; break;
  198.                 case '=': type = T_MINUSEQUALS; break;
  199.                 default: type = T_MINUS; reread();
  200.             }
  201.             break;
  202.         case '*':
  203.             switch (nextchar()) {
  204.                 case '=': type = T_MULTEQUALS; break;
  205.                 case '*':
  206.                     switch (nextchar()) {
  207.                         case '=': type = T_POWEREQUALS; break;
  208.                         default: type = T_POWER; reread();
  209.                     }
  210.                     break;
  211.                 default: type = T_MULT; reread();
  212.             }
  213.             break;
  214.         case '/':
  215.             switch (nextchar()) {
  216.                 case '/':
  217.                     switch (nextchar()) {
  218.                         case '=': type = T_SLASHSLASHEQUALS; break;
  219.                         default: reread(); type = T_SLASHSLASH; break;
  220.                     }
  221.                     break;
  222.                 case '=': type = T_DIVEQUALS; break;
  223.                 case '*': eatcomment(); break;
  224.                 default: type = T_DIV; reread();
  225.             }
  226.             break;
  227.         case '%':
  228.             switch (nextchar()) {
  229.                 case '=': type = T_MODEQUALS; break;
  230.                 default: type = T_MOD; reread();
  231.             }
  232.             break;
  233.         case '<':
  234.             switch (nextchar()) {
  235.                 case '=': type = T_LE; break;
  236.                 case '<':
  237.                     switch (nextchar()) {
  238.                         case '=': type = T_LSHIFTEQUALS; break;
  239.                         default:  reread(); type = T_LEFTSHIFT; break;
  240.                     }
  241.                     break;
  242.                 default: type = T_LT; reread();
  243.             }
  244.             break;
  245.         case '>':
  246.             switch (nextchar()) {
  247.                 case '=': type = T_GE; break;
  248.                 case '>':
  249.                     switch (nextchar()) {
  250.                         case '=': type = T_RSHIFTEQUALS; break;
  251.                         default:  reread(); type = T_RIGHTSHIFT; break;
  252.                     }
  253.                     break;
  254.                 default: type = T_GT; reread();
  255.             }
  256.             break;
  257.         case '&':
  258.             switch (nextchar()) {
  259.                 case '&': type = T_ANDAND; break;
  260.                 case '=': type = T_ANDEQUALS; break;
  261.                 default: type = T_AND; reread(); break;
  262.             }
  263.             break;
  264.         case '|':
  265.             switch (nextchar()) {
  266.                 case '|': type = T_OROR; break;
  267.                 case '=': type = T_OREQUALS; break;
  268.                 default: type = T_OR; reread(); break;
  269.             }
  270.             break;
  271.         case '!':
  272.             switch (nextchar()) {
  273.                 case '=': type = T_NE; break;
  274.                 default: type = T_NOT; reread(); break;
  275.             }
  276.             break;
  277.         case '\\':
  278.             switch (nextchar()) {
  279.                 case '\n': setprompt(PROMPT2); break;
  280.                 default: scanerror(T_NULL, "Unknown token character '%c'", ch);
  281.             }
  282.             break;
  283.         default:
  284.             if (isletter(ch)) {
  285.                 reread();
  286.                 type = eatsymbol();
  287.                 break;
  288.             }
  289.             if (isdigit(ch) || (ch == '.')) {
  290.                 reread();
  291.                 type = eatnumber();
  292.                 break;
  293.             }
  294.             scanerror(T_NULL, "Unknown token character '%c'", ch);
  295.         }
  296.     }
  297.     curtoken.t_type = (short)type;
  298.     return type;
  299. }
  300.  
  301.  
  302. /*
  303.  * Continue to eat up a comment string.
  304.  * The leading slash-asterisk has just been scanned at this point.
  305.  */
  306. static void
  307. eatcomment()
  308. {
  309.     int ch;
  310.  
  311.     for (;;) {
  312.         ch = nextchar();
  313.         if (ch == '*') {
  314.             ch = nextchar();
  315.             if (ch == '/')
  316.                 return;
  317.             reread();
  318.         }
  319.         if ((ch == EOF) || (ch == '\0') ||
  320.             (newlines && (ch == '\n') && inputisterminal())) {
  321.                 reread();
  322.                 scanerror(T_NULL, "Unterminated comment");
  323.                 return;
  324.         }
  325.     }
  326. }
  327.  
  328.  
  329. /*
  330.  * Read in a string and add it to the literal string pool.
  331.  * The leading single or double quote has been read in at this point.
  332.  */
  333. static void
  334. eatstring(quotechar)
  335.     int quotechar;
  336. {
  337.     register char *cp;    /* current character address */
  338.     int ch;            /* current character */
  339.     char buf[MAXSTRING+1];    /* buffer for string */
  340.  
  341.     cp = buf;
  342.     for (;;) {
  343.         ch = nextchar();
  344.         switch (ch) {
  345.             case '\0':
  346.             case EOF:
  347.             case '\n':
  348.                 reread();
  349.                 scanerror(T_NULL, "Unterminated string constant");
  350.                 *cp = '\0';
  351.                 curtoken.t_str = addliteral(buf);
  352.                 return;
  353.  
  354.             case '\\':
  355.                 ch = nextchar();
  356.                 switch (ch) {
  357.                     case 'n': ch = '\n'; break;
  358.                     case 'r': ch = '\r'; break;
  359.                     case 't': ch = '\t'; break;
  360.                     case 'b': ch = '\b'; break;
  361.                     case 'f': ch = '\f'; break;
  362.                     case '\n':
  363.                         setprompt(PROMPT2);
  364.                         continue;
  365.                     case EOF:
  366.                         reread();
  367.                         continue;
  368.                 }
  369.                 *cp++ = (char)ch;
  370.                 break;
  371.  
  372.             case '"':
  373.             case '\'':
  374.                 if (ch == quotechar) {
  375.                     *cp = '\0';
  376.                     curtoken.t_str = addliteral(buf);
  377.                     return;
  378.                 }
  379.                 /* fall into default case */
  380.  
  381.             default:
  382.                 *cp++ = (char)ch;
  383.         }
  384.     }
  385. }
  386.  
  387.  
  388. /*
  389.  * Read in a symbol name which may or may not be a keyword.
  390.  * If allsyms is set, keywords are not looked up and almost all chars
  391.  * will be accepted for the symbol.  Returns the type of symbol found.
  392.  */
  393. static int
  394. eatsymbol()
  395. {
  396.     register struct keyword *kp;    /* pointer to current keyword */
  397.     register char *cp;        /* current character pointer */
  398.     int ch;                /* current character */
  399.     int cc;                /* character count */
  400.     static char buf[SYMBOLSIZE+1];    /* temporary buffer */
  401.  
  402.     cp = buf;
  403.     cc = SYMBOLSIZE;
  404.     if (allsyms) {
  405.         for (;;) {
  406.             ch = nextchar();
  407.             if ((ch == ' ') || (ch == ';') || (ch == '\n'))
  408.                 break;
  409.             if (cc-- > 0)
  410.                 *cp++ = (char)ch;
  411.         }
  412.         reread();
  413.         *cp = '\0';
  414.         if (cc < 0)
  415.             scanerror(T_NULL, "Symbol too long");
  416.         curtoken.t_str = buf;
  417.         return T_SYMBOL;
  418.     }
  419.     for (;;) {
  420.         ch = nextchar();
  421.         if (!issymbol(ch))
  422.             break;
  423.         if (cc-- > 0)
  424.             *cp++ = (char)ch;
  425.     }
  426.     reread();
  427.     *cp = '\0';
  428.     if (cc < 0)
  429.         scanerror(T_NULL, "Symbol too long");
  430.     for (kp = keywords; kp->k_name; kp++)
  431.         if (strcmp(kp->k_name, buf) == 0)
  432.             return kp->k_token;
  433.     curtoken.t_str = buf;
  434.     return T_SYMBOL;
  435. }
  436.  
  437.  
  438. /*
  439.  * Read in and remember a possibly numeric constant value.
  440.  * The constant is inserted into a constant table so further uses
  441.  * of the same constant will not take more memory.  This can also
  442.  * return just a period, which is used for element accesses and for
  443.  * the old numeric value.
  444.  */
  445. static int
  446. eatnumber()
  447. {
  448.     register char *cp;    /* current character pointer */
  449.     long len;        /* parsed size of number */
  450.     long res;        /* result of parsing number */
  451.  
  452.     if (numbufsize == 0) {
  453.         numbuf = (char *)malloc(128+1);
  454.         if (numbuf == NULL)
  455.             math_error("Cannot allocate number buffer");
  456.         numbufsize = 128;
  457.     }
  458.     cp = numbuf;
  459.     len = 0;
  460.     for (;;) {
  461.         if (len >= numbufsize) {
  462.             cp = (char *)realloc(numbuf, numbufsize + 1001);
  463.             if (cp == NULL)
  464.                 math_error("Cannot reallocate number buffer");
  465.             numbuf = cp;
  466.             numbufsize += 1000;
  467.             cp = &numbuf[len];
  468.         }
  469.         *cp = nextchar();
  470.         *(++cp) = '\0';
  471.         if ((numbuf[0] == '.') && isletter(numbuf[1])) {
  472.             reread();
  473.             return T_PERIOD;
  474.         }
  475.         res = qparse(numbuf, QPF_IMAG);
  476.         if (res < 0) {
  477.             reread();
  478.             scanerror(T_NULL, "Badly formatted number");
  479.             curtoken.t_numindex = addnumber("0");
  480.             return T_NUMBER;
  481.         }
  482.         if (res != ++len)
  483.             break;
  484.     }
  485.     cp[-1] = '\0';
  486.     reread();
  487.     if ((numbuf[0] == '.') && (numbuf[1] == '\0')) {
  488.         curtoken.t_numindex = 0;
  489.         return T_OLDVALUE;
  490.     }
  491.     cp -= 2;
  492.     res = T_NUMBER;
  493.     if ((*cp == 'i') || (*cp == 'I')) {
  494.         *cp = '\0';
  495.         res = T_IMAGINARY;
  496.     }
  497.     curtoken.t_numindex = addnumber(numbuf);
  498.     return res;
  499. }
  500.  
  501.  
  502. /*
  503.  * Return the string value of the current token.
  504.  */
  505. char *
  506. tokenstring()
  507. {
  508.     return curtoken.t_str;
  509. }
  510.  
  511.  
  512. /*
  513.  * Return the constant index of a numeric token.
  514.  */
  515. long
  516. tokennumber()
  517. {
  518.     return curtoken.t_numindex;
  519. }
  520.  
  521.  
  522. /*
  523.  * Push back the token just read so that it will be seen again.
  524.  */
  525. void
  526. rescantoken()
  527. {
  528.     rescan = TRUE;
  529. }
  530.  
  531.  
  532. /*
  533.  * Describe an error message.
  534.  * Then skip to the next specified token (or one more powerful).
  535.  */
  536. #ifdef VARARGS
  537. # define VA_ALIST skip, fmt, va_alist
  538. # define VA_DCL int skip; char *fmt; va_dcl
  539. #else
  540. # if defined(__STDC__) && __STDC__ == 1
  541. #  define VA_ALIST int skip, char *fmt, ...
  542. #  define VA_DCL
  543. # else
  544. #  define VA_ALIST skip, fmt
  545. #  define VA_DCL int skip; char *fmt;
  546. # endif
  547. #endif
  548. /*VARARGS*/
  549. void
  550. scanerror(VA_ALIST)
  551.     VA_DCL
  552. {
  553.     va_list ap;
  554.     char *name;        /* name of file with error */
  555.     char buf[MAXERROR+1];
  556.  
  557.     errorcount++;
  558.     name = inputname();
  559.     if (name)
  560.         fprintf(stderr, "\"%s\", line %ld: ", name, linenumber());
  561. #ifdef VARARGS
  562.     va_start(ap);
  563. #else
  564.     va_start(ap, fmt);
  565. #endif
  566.     vsprintf(buf, fmt, ap);
  567.     va_end(ap);
  568.     fprintf(stderr, "%s\n", buf);
  569.     switch (skip) {
  570.         case T_NULL:
  571.             return;
  572.         case T_COMMA:
  573.             rescan = TRUE;
  574.             for (;;) {
  575.                 switch (gettoken()) {
  576.                 case T_NEWLINE:
  577.                 case T_SEMICOLON:
  578.                 case T_LEFTBRACE:
  579.                 case T_RIGHTBRACE:
  580.                 case T_EOF:
  581.                 case T_COMMA:
  582.                     rescan = TRUE;
  583.                     return;
  584.                 }
  585.             }
  586.         default:
  587.             fprintf(stderr, "Unknown skip token for scanerror\n");
  588.             /* fall into semicolon case */
  589.             /*FALLTHRU*/
  590.         case T_SEMICOLON:
  591.             rescan = TRUE;
  592.             for (;;) switch (gettoken()) {
  593.                 case T_NEWLINE:
  594.                 case T_SEMICOLON:
  595.                 case T_LEFTBRACE:
  596.                 case T_RIGHTBRACE:
  597.                 case T_EOF:
  598.                     rescan = TRUE;
  599.                     return;
  600.             }
  601.     }
  602. }
  603.  
  604. /* END CODE */
  605.